package demoMod.anm2editor.enums;

import com.badlogic.gdx.math.Interpolation;
import demoMod.anm2editor.model.Anchor;

import java.util.ArrayList;
import java.util.List;

/**
 * 路径类型
 */
public enum CustomPathType {
    BROKEN_LINE {
        @Override
        public Anchor apply(List<Anchor> anchors, float a) {
            List<Float> distances = new ArrayList<>();
            float sum = 0.0F;
            for (Anchor anchor : anchors) {
                if (anchors.indexOf(anchor) != anchors.size() - 1) {
                    Anchor nextAnchor = anchors.get(anchors.indexOf(anchor) + 1);
                    float dist = (float) Math.sqrt(Math.pow(anchor.getX() - nextAnchor.getX(), 2) + Math.pow(anchor.getY() - nextAnchor.getY(), 2));
                    distances.add(dist);
                    sum += dist;
                }
            }
            float target = sum * a;
            int index = 0;
            for (Float dist : distances) {
                if (target - dist <= 0.0F) {
                    Anchor start = anchors.get(index);
                    Anchor end = anchors.get(index + 1);
                    Anchor ret = new Anchor();
                    ret.setX(Interpolation.linear.apply(start.getX(), end.getX(), target / dist));
                    ret.setY(Interpolation.linear.apply(start.getY(), end.getY(), target / dist));
                    return ret;
                }
                target -= dist;
                index++;
            }
            Anchor ret = new Anchor();
            ret.setX(anchors.get(anchors.size() - 1).getX());
            ret.setY(anchors.get(anchors.size() - 1).getY());
            return ret;
        }
    },
    PEN {
        private final ThreadLocal<Float> cachedLength = ThreadLocal.withInitial(() -> null);
        private final ThreadLocal<List<Float>> cachedDistances = ThreadLocal.withInitial(() -> null);
        private final ThreadLocal<List<Anchor>> cachedPoints = ThreadLocal.withInitial(() -> null);

        @Override
        public Anchor apply(List<Anchor> anchors, float a) {
            buildCache(anchors);
            float target = cachedLength.get() * a;
            int index = 0;
            for (Float dist : cachedDistances.get()) {
                if (target - dist <= 0) {
                    Anchor start = cachedPoints.get().get(index);
                    Anchor end = cachedPoints.get().get(index + 1);
                    Anchor ret = new Anchor();
                    ret.setX(Interpolation.linear.apply(start.getX(), end.getX(), target / dist));
                    ret.setY(Interpolation.linear.apply(start.getY(), end.getY(), target / dist));
                    return ret;
                }
                target -= dist;
                index++;
            }
            Anchor ret = new Anchor();
            ret.setX(anchors.get(anchors.size() - 1).getX());
            ret.setY(anchors.get(anchors.size() - 1).getY());
            return ret;
        }

        @Override
        public void clearCache() {
            cachedLength.set(null);
            cachedDistances.set(null);
            cachedPoints.set(null);
        }

        private void buildCache(List<Anchor> anchors) {
            if (cachedLength.get() != null && cachedDistances.get() != null && cachedPoints.get() != null) {
                return;
            }
            cachedLength.set(0.0F);
            cachedDistances.set(new ArrayList<>());
            cachedPoints.set(new ArrayList<>());
            List<Float> lineLengths = new ArrayList<>();
            for (Anchor anchor : anchors) {
                if (anchors.indexOf(anchor) != anchors.size() - 1) {
                    Anchor nextAnchor = anchors.get(anchors.indexOf(anchor) + 1);
                    float dist = (float) Math.sqrt(Math.pow(anchor.getX() - nextAnchor.getX(), 2) + Math.pow(anchor.getY() - nextAnchor.getY(), 2));
                    lineLengths.add(dist);
                }
            }

            int index = 0;
            for (Anchor anchor : anchors) {
                if (index < anchors.size() - 1) {
                    int segmentNum = (int) Math.floor(lineLengths.get(index) / 5.0F);
                    Anchor nextAnchor = anchors.get(index + 1);
                    List<Anchor> subAnchors = new ArrayList<>();
                    subAnchors.add(anchor);
                    subAnchors.add(anchor.getBezierPoints().get(anchor.getBezierPoints().size() - 1));
                    subAnchors.add(nextAnchor.getBezierPoints().get(0));
                    subAnchors.add(nextAnchor);
                    for (int i=0;i<segmentNum;i++) {
                        Anchor res = apply0(subAnchors, i / (float) segmentNum);
                        cachedPoints.get().add(res);
                    }
                }
                index++;
            }
            index = 0;
            for (Anchor anchor : cachedPoints.get()) {
                if (index < cachedPoints.get().size() - 1) {
                    Anchor nextAnchor = cachedPoints.get().get(index + 1);
                    float dist = (float) Math.sqrt(Math.pow(anchor.getX() - nextAnchor.getX(), 2) + Math.pow(anchor.getY() - nextAnchor.getY(), 2));
                    cachedDistances.get().add(dist);
                    cachedLength.set(cachedLength.get() + dist);
                }
                index++;
            }
        }

        private Anchor apply0(List<Anchor> anchors, float a) {
            if (anchors.size() > 2) {
                List<Anchor> subAnchors = new ArrayList<>();
                for (Anchor anchor : anchors) {
                    if (anchors.indexOf(anchor) != anchors.size() - 1) {
                        Anchor nextAnchor = anchors.get(anchors.indexOf(anchor) + 1);
                        Anchor newAnchor = new Anchor();
                        newAnchor.setX((nextAnchor.getX() - anchor.getX()) * a + anchor.getX());
                        newAnchor.setY((nextAnchor.getY() - anchor.getY()) * a + anchor.getY());
                        subAnchors.add(newAnchor);
                    }
                }
                return apply0(subAnchors, a);
            }
            Anchor ret = new Anchor();
            ret.setX(Interpolation.linear.apply(anchors.get(0).getX(), anchors.get(1).getX(), a));
            ret.setY(Interpolation.linear.apply(anchors.get(0).getY(), anchors.get(1).getY(), a));
            return ret;
        }
    }
    ;
    public abstract Anchor apply(List<Anchor> anchors, float a);

    public void clearCache() {
    }
}
